Skip to content

fix(session): require valid JWT expiration - #442

Merged
gjtorikian merged 4 commits into
mainfrom
workgraph/workos-php-37e-5013d0d2
Sep 16, 2026
Merged

gjtorikian merged 4 commits into
mainfrom
workgraph/workos-php-37e-5013d0d2

Conversation

@gjtorikian

Copy link
Copy Markdown
Contributor

Summary

  • Fail closed after signature verification when exp is missing, null, or non-numeric, preventing signed session tokens from bypassing expiration checks.
  • Reject expired tokens, including the exact exp == time() boundary, while preserving numeric-string compatibility.
  • Cover 15 expiration cases with signed JWTs and an isolated, deterministic test clock; invalid tokens retain the public invalid_jwt response.
  • Keep this fix exp-only: iss/aud/nbf validation is intentionally deferred. Issuer validation is tracked separately in feat: Add optional issuer check to SessionManager authenticate #440, and the existing issuer/audience TODO remains unchanged.

Validation

composer ci passes: PHP-CS-Fixer reports 0 fixable files, PHPStan reports 0 errors, and PHPUnit reports 366 tests and 1,708 assertions. The two original missing-fixture skips are pre-existing and unrelated; current main adds a third (OrganizationsTest::testListItContacts). All 15 expiration cases pass.

Current main was merged without conflicts to exclude an inherited, already-merged PKCE change from the PR diff. Only lib/SessionManager.php, tests/SessionManagerTest.php, and tests/Fixtures/session_expiration_clock.php differ from main.

gjtorikian and others added 3 commits July 27, 2026 10:54
PKCEHelper's four flows (AuthKit/SSO authorization URL and code
exchange) required callers to re-pass a clientId the WorkOS client
already carries (constructor arg / WORKOS_CLIENT_ID). Make the
parameter optional with a fallback to requireClientId(), aligning PHP
with the other backend SDKs' override-with-fallback pattern. Explicit
arguments still win; an unconfigured client now throws
ConfigurationException instead of a TypeError.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed session tokens without a numeric expiration could bypass expiry
validation indefinitely. Fail closed while preserving numeric-string
compatibility, and reject tokens at the expiration boundary.

Addresses VULN-1270.
@gjtorikian
gjtorikian requested review from a team as code owners September 10, 2026 20:49
@gjtorikian
gjtorikian requested a review from mattgd September 10, 2026 20:49
@greptile-apps

greptile-apps Bot commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 5/5

The PR appears safe to merge; the previously reported fractional-expiration issue is fully fixed and no new actionable failures remain.

Summary

This PR strengthens session JWT expiration validation and adds deterministic coverage for expiration edge cases.

  • Requires the signed JWT to contain a numeric exp claim.
  • Rejects expiration at or before the current time.
  • Preserves fractional NumericDate precision by comparing as a float.
  • Adds isolated tests for malformed, expired, boundary, numeric-string, and fractional claims.
Diagram
%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Open sealed session] --> B[Decode access-token JWT]
    B --> C[Verify algorithm and signature]
    C --> D{Numeric exp present?}
    D -- No --> E[Return invalid_jwt]
    D -- Yes --> F{exp greater than current time?}
    F -- No --> E
    F -- Yes --> G[Return authenticated session]
Loading

Reviews (2) · Last reviewed commit: "fix(session): compare exp as float to ke..."

Comment thread lib/SessionManager.php Outdated
Casting exp to int before the boundary check truncated fractional
NumericDate values, so a token expiring at 1700000000.5 was rejected at
1700000000 even though it was still valid. Compare as float instead and
cover both sides of the fractional boundary in the expiration matrix.
@gjtorikian
gjtorikian merged commit 717b3cf into main Sep 16, 2026
9 checks passed
@gjtorikian
gjtorikian deleted the workgraph/workos-php-37e-5013d0d2 branch September 16, 2026 22:06
@linear-code

linear-code Bot commented Sep 17, 2026

Copy link
Copy Markdown

VULN-1270

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant